import { Config, Context, Inject, Provide } from '@midwayjs/core';
import { LoginDTO } from '../dto/login';
import { RedisService } from '@midwayjs/redis';
import { R } from '../../../common/base.error.util';
import * as bcrypt from 'bcryptjs';
import { UserEntity } from '../../user/entity/user';
import { Repository } from 'typeorm';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { uuid } from '../../../utils/uuid';
import { RSAService } from '../../../common/rsa.service';
import { ResetPasswordDTO } from '../dto/reset.password';

export interface TokenConfig {
  expire: number;
  refreshExpire: number;
}

@Provide()
export class AuthService {
  @InjectEntityModel(UserEntity)
  userModel: Repository<UserEntity>;

  @Inject()
  redisService: RedisService;

  @Config('token')
  tokenConfig: TokenConfig;

  @Inject()
  rsaService: RSAService;

  @Inject()
  ctx: Context;

  async login(loginDTO: LoginDTO) {
    const { userName, publicKey, password } = loginDTO;
    const pwd = await this.rsaService.decrypt(publicKey, password);
    const user = await this.userModel
      .createQueryBuilder('user')
      .where('user.userName = :userName', {
        userName,
      })
      .select(['user.password', 'user.id'])
      .getOne();

    if (!user) {
      throw R.error('账号或密码错误！');
    }

    if (!bcrypt.compareSync(pwd, user.password)) {
      throw R.error('账号或密码错误！');
    }

    const { expire, refreshExpire } = this.tokenConfig;

    const token = uuid();
    const refreshToken = uuid();

    // multi可以实现redis指令并发执行
    await this.redisService
      .multi()
      .set(`token:${token}`, user.id)
      .expire(`token:${token}`, expire)
      .set(`refreshToken:${refreshToken}`, user.id)
      .expire(`refreshToken:${refreshToken}`, refreshExpire)
      .exec();

    return {
      expire,
      token,
      refreshExpire,
      refreshToken,
    };
  }
  async refreshToken(refreshToken: string) {
    const userId = await this.redisService.get(
      `refreshToken:${refreshToken}`
    );

    if (!userId) {
      throw R.error('刷新token失败');
    }

    const { expire } = this.tokenConfig;

    const token = uuid();

    await this.redisService
      .multi()
      .set(`token:${token}`, userId)
      .expire(`token:${token}`, expire)
      .exec();

    const refreshExpire = await this.redisService.ttl(
      `refreshToken:${refreshToken}`
    );

    return {
      expire,
      token,
      refreshExpire,
      refreshToken: refreshToken,
    }
  }
  async resetPassword(resetPasswordDTO: ResetPasswordDTO) {
    const pwd = await this.rsaService.decrypt(
      resetPasswordDTO.publicKey,
      resetPasswordDTO.password
    );

    const hashPassword = bcrypt.hashSync(pwd, 10);
    const entity = await this.userModel.findOneBy({ id: this.ctx.userInfo.userId });
    entity.password = hashPassword
    await this.userModel.save(entity);
  }
}
